/*@S.Genikhov*/
/*Simulation of Memory Manager*/
/*Creation Data*?
/*Modifying Data:
/*Brief: Goal of Memory Manager is cooredate the memory consumtion from some initially taken memory block by different task needs.
Manager gets the order to get or free memory and findes the apropriative memory blockes from initialli allocated part.
The MAP of used/non used memory block exist in bit vector. */
/*Algorithm malloc:
0.Adds to required memory addition 2 biats , will be surved to write the how bytes is used
1.Go to bitvector and find the 1st zero non used part
2.Checks is it abble to allocat the required memory lenght from this point
3.If yes, the  wright on 1st 2 baits on Memory used to designate how much will be used
4.All used bytes will set to 1 on bitvector
5.if at point 3 is no, looks for the 1st location of 0 on bit vector and steps 4, 5 povtorjajutsja
/*Algorithm to Free memory*/
/*1.Read the memori size to delete from 2bytes preceedings the memory pointer
/*2.Determined the bit vector offset the sameofset as on memory block
/*3.Change in bitvector the 1 on 0 in relative region*/

#include <stdlib.h>
#include <bitvector.h>
#include <MemoryManager.h>



unsigned int MM_ConvSize2UnitsSize(size_t _requstsize,size_t _unitsize);

err_MM MM_ClearBitVector(void* bv,size_t _index, size_t _size)
{
    size_t      i;
    for(i=0;i<_size;++i)
    {
        Bitv_ClearBit((unsigned char *)bv,_index+i);
    }
    return 0;
}


err_MM MM_Free(MMheap* _memmang,void* _mem2free)
{
    unsigned int      offset;
    short     size2free;

    if(_memmang==0)
    {
        return MM_NOT_ACCEPT_MEM_MANAGER;
    }

    if(_mem2free==0)
    {
        return MM_NOT_ACCEPT_MEM_TO_FREE;
    }

    /*Step 1Free*/
    size2free=*(((unsigned short*)_mem2free-1));/*allready in unitsize*/
    /*Step 2 Free*/
    offset=((((unsigned char*)_mem2free))-2)-((unsigned char*)_memmang->memory);/*it must be the multiplies of unitsize*/
    /*need to convert to offset in unitsize*/
    offset=MM_ConvSize2UnitsSize(offset,MEM_UNIT);
     /*Step 3Free*/
   MM_ClearBitVector( _memmang->bv,offset,size2free);
    return 0;
}



err_MM MM_SetBitVector(void* bv,size_t _index, size_t _size)/*Step 4*/
{
    size_t     i;

    for(i=0;i<_size;++i)
    {
        Bit_Set((unsigned char *)bv,_index+i);
    }
    return 0;
}

/*step 3*/
err_MM MM_SetMemTakenSize(void* _memory,unsigned short _size)
{
    *((unsigned short*) _memory)=_size;/*THe 1st 2 Baits in memory block serves for storing the allocation size*/
    return 0;
}

/*size_t _size in  unitsize dimentions*/
void* MM_MemoryAllocation(unsigned char* _bv,void* _memory, size_t _size,INOUT err_MM* _err)/*size- memory to allocate in unitsizes*/
{
    size_t        index;
    size_t        lenght;
    size_t        bitveclenght=(MM_HEAP_SIZE/(MEM_UNIT*8));
    unsigned char isbuisyspace[2];

    if(_bv==0)
    {
        *_err=MM_BITVECTOR_ALLOCATION_PROBLEMS;
        return 0;
    }

    if(_memory==0)
    {
        *_err=MM_MALLOC_MEMORY_PROBLEMS;
        return 0;
    }

    if(_size<0 || _size>=(MM_HEAP_SIZE/MEM_UNIT))
    {
        *_err=MM_TO_LARGE_MEMORY_REQUEST;
        return 0;
    }

    index=0;
    while(1)
    {
        /*Step1*/
        Bit_Getvalue(_bv,&isbuisyspace[0],index,1);
        for(;index<bitveclenght && isbuisyspace[0];++index)
        {
            Bit_Getvalue(_bv,&isbuisyspace[0],index,1);
        }
        if(index<(bitveclenght-_size))
        {
            /*Step 2*/

            if(index)
            {
                --index;
            }
            lenght=_size+index;
            isbuisyspace[0]=0;
            for(;index<(lenght) && !isbuisyspace[0];++index)
            {
                Bit_Getvalue(_bv,&isbuisyspace[0],index,1);
            }
            if (index==lenght)
            {
                break;
            }
        }
        else
        {
            *_err=MM_NON_ENOUGH_MEMORY;

            return 0;
        }
    }
    /*Step 3*/
    index=index-_size;/*index at the end of memoryblock*/
   *_err=MM_SetMemTakenSize(&(((unsigned char*) _memory)[MEM_UNIT*(index)]),_size);
    if(!(*_err))
    {
            *_err=MM_SetBitVector(_bv,index,_size);
    }
    if(!(*_err))
    {
        return &(((unsigned char*) _memory)[MEM_UNIT*(index)]);
    }
    return 0;
}

/*convert to number of units*/
size_t MM_ConvSize2UnitsSize(size_t _requstsize,size_t _unitsize)
{
    if(_requstsize%_unitsize)
    {
     return (_requstsize/_unitsize+1);
    }
    return _requstsize/_unitsize;
}

void* MM_Malloc(MMheap* _memmang,size_t _size,INOUT err_MM* err)
{
    size_t reqestmemsize;
    void* allocatedmemory;
    if(_size>0)
    {
        if(_memmang==0)
        {
            *err=MM_MANAGER_ALLOC_PROBLEMS;
            return 0;
            }
            _size+=2;

        reqestmemsize=MM_ConvSize2UnitsSize(_size,_memmang->unitsize);
        allocatedmemory=MM_MemoryAllocation(_memmang->bv,_memmang->memory,reqestmemsize,INOUT err);
        return &(((unsigned short*)allocatedmemory)[1]); /*returns to pointer to 3td byte in memory, 1st tow served to store the allocated lenght*/
    }
    return 0;
}

MMheap* MM_Create(size_t _memsize, size_t _unitsize)
{
    MMheap* memmang     =malloc(sizeof(MMheap));/*Memmanager*/
    if(memmang==0)
    {
        return 0;/*Manager criation problems*/
    }

    memmang->memory     =(void*) malloc(sizeof(char)*_memsize);/*size of alloc memeory per one task*/
    memmang->unitsize   =_unitsize;
    memmang->size       =_memsize;
    memmang->bv         =calloc(((_memsize/8)/_unitsize),sizeof(char));
    return memmang;
}
void MM_Destroy(MMheap* _memmang)
{
     free(_memmang->bv);
     free(_memmang->memory);
#ifdef _Debug_of_Schelduller
     printf("\n FREE address of _memmang->bv is      %x ",  _memmang->bv);
     printf("\n FREE address of _memmang->memory is  %x ",  _memmang->memory);
#endif /*_Debug_of_Schelduller*/
}

#ifdef MEM_MANAG_UTEST
#define is_success(x) x? "Failure":"Success"

int MM_unitest();
err_MM Test_MM_Malloc();
err_MM Test_MM_Free();


int main()
{
    MM_unitest();
    return 0;
}

int MM_unitest()
{
    err_MM  err1=Test_MM_Malloc();
    err_MM  err2=Test_MM_Free();
    printf("\nThe Test of MM_Malloc is : %s \n", is_success(err1));
    printf("\nThe Test of MM_Free is : %s \n",   is_success(err2));
    if (!err1 && !err2)
    {
         printf("\nThe MemoryManger  is : OK \n");
    }

    return 0;
}


err_MM Test_MM_Malloc()
{
    char*                    memoryblock[4];
    unsigned short*          m1;
    unsigned short*          m2;
    unsigned short*          m3;
    err_MM    err;
    /*i will allocat 20,100 and 200 bytes of memory
    sizeof block have to be 24,104 and 200 respectively*/

    MMheap* memman=MM_Create(MM_HEAP_SIZE,MEM_UNIT);
    memoryblock[0]=MM_Malloc(memman,20,&err);

    if(!err)
    {
        memoryblock[1]=MM_Malloc(memman,100,&err);
    }
    if(!err)
    {
    memoryblock[2]=MM_Malloc(memman,200,&err);
    }
    if(!err)
    {
    memoryblock[3]=MM_Malloc(memman,100,&err);
    }
    if((memoryblock[1])-(memoryblock[0])==24 && (memoryblock[3])-(memoryblock[0])==336)
    {
        m1=(unsigned short*) (memoryblock[0]);
        m2=(unsigned short*) (memoryblock[1]);
        m3=(unsigned short*) (memoryblock[2]);
        if(*(m1-1)==3 && *(m2-1)==13 && *(m3-1)==26)
         {return 0;}

    }

    return 1;
}

err_MM Test_MM_Free()
{
    char*                    memoryblock[8];
    err_MM    err;
    /*i will allocat 20,100 and 200 bytes of memory
    sizeof block have to be 24,104 and 200 respectively*/

    MMheap* memman=MM_Create(MM_HEAP_SIZE,MEM_UNIT);
    memoryblock[0]=MM_Malloc(memman,20,&err);

    if(!err)
    {
        memoryblock[1]=MM_Malloc(memman,100,&err);
    }
    if(!err)
    {
    memoryblock[2]=MM_Malloc(memman,200,&err);
    }
    if(!err)
    {
    memoryblock[3]=MM_Malloc(memman,100,&err);
    }

    MM_Free(memman,memoryblock[0]);
    MM_Free(memman,memoryblock[2]);
    memoryblock[4]=MM_Malloc(memman,100,&err);/*Same place were was memoryblock[2]*/
    memoryblock[5]=MM_Malloc(memman,10,&err);/*Same place were was memoryblock[0]*/
    memoryblock[6]=MM_Malloc(memman,300,&err);/*Neew block*/
    memoryblock[7]=MM_Malloc(memman,500,&err);

    if(memoryblock[4]==memoryblock[2] && memoryblock[5]==memoryblock[0] && memoryblock[7]-memoryblock[0]==744)
    {
        return 0;
    }
    MM_Destroy(memman);
    free(memman);
    return 1;
}



#endif/*MEM_MANAG_UTEST*/
